import {
	cloneElement,
	FC,
	useState,
	SyntheticEvent,
	useMemo,
	memo,
	useCallback,
} from 'react'
import { defaultProps, thisType } from '../types/baseType.ts'

interface Obj {
	value: string
}

type Props = defaultProps & {
	value: string
	onChange: (e: string) => void
	obj?: Obj
}

const Input: FC<Props> = ({ value, onChange }) => {
	console.log('Input 重渲染')
	return cloneElement(<input />, {
		placeholder: 'haha',
		value,
		onChange: (e: SyntheticEvent) => {
			onChange((e.target as thisType).value)
		},
	})
}

const MemoInput = memo<Props>(({ onChange, obj = { value: '' } }) => (
	<Input value={obj.value} onChange={onChange} />
))

export default function CloneElement() {
	// 考点1：useState 只初始化 1次，后续重复渲染只是重新去 fiber hook 单链表上拿 value 和 setValue 方法。因此 setValue 方法一直是同一个。
	const [value, setValue] = useState('')
	const [count, setCount] = useState(0)
	const [objValue, setObjValue] = useState({ value: '' })
	const [arrValue, setArrValue] = useState(['1', '2'])

	const setCountFn = () => {
		setCount((preCount) => preCount + 1)
		// 非不可变值，会导致 bug 产生
		// setObjValue((preObj) => { preObj.value = '1'; return preObj })
		// 不可变值能避免 bug
		// setObjValue((preObj) => { return { ...preObj } })
		setArrValue((preObj) => {
			return ['3', ...preObj]
		})
	}

	// 考点2：这里 obj 需要缓存，防止父组件重渲染会重新生成新的对象。useMemo 还用于缓存逻辑复杂、计算比较大的场景，防止重新计算。
	//const obj = { value }
	//const obj = useMemo<Obj>(() => { return { value } }, [value])

	// 考点3：依赖对象时，如果修改对象没有使用不可变值，会导致修改的实际对象还是原始对象，导致 useMemo 失效。保险起见还是依赖对象里面的值。
	// 可以将依赖对象转换成字符串，或者使用 useDeepCompareEffect 等自定义 hook。 参考链接：https://juejin.cn/post/6885957893724569613。
	// const obj = useMemo<Obj>(() => { return { ...objValue } }, [objValue.value])

	// 考点4：依赖数组时，如果依赖数组的每一项，那么可以直接写依赖数组（考虑修改数据都是不可变值），如果只依赖数组中的某一项，可以指定某一项依赖。
	const obj = useMemo<Obj>(() => {
		return { value: arrValue[0] }
	}, [arrValue[0]])

	// 考点5：子定义函数传给子组件，需要缓存起来，否则父组件重渲染，每次都生成新的函数，导致子组件重渲染。
	const setValueFn = useCallback((str: string) => setValue(str), [])
	return (
		<div>
			<h3>CloneElement</h3>
			<div>count: {count}</div>
			<input type="button" value="setCount" onClick={setCountFn} />
			<MemoInput value={value} onChange={setValueFn} obj={obj} />
			<hr />
		</div>
	)
}
